<?php
namespace App\Models;

use App\Models\ComModel;
use App\Models\CronModel;
use App\Models\UserModel;

class NoticeModel extends ComModel
{

    public $tablename;

    public function __construct()
    {
        parent::__construct();
        $this->tablename = 'notice';
    }

    private function _validate($data, $id = 0)
    {
        if (!$data) {
            return ams_rt(1, '请填写信息');
        }
        $data = esc($data);
        if (!$data['name']) {
            return ams_rt(1, '名称必须填写');
        } elseif (!$data['code']) {
            return ams_rt(1, '代码必须填写');
        }

        $flag = $this->field_exitsts($this->tablename, 'name', $data['name'], $id);
        if ($flag) {
            return ams_rt(1, '站内信已存在');
        }

        return ams_rt(0, 'ok', $data);
    }

    public function add($data)
    {
        $rt = $this->_validate($data);
        if ($rt['code']) {
            return $rt;
        }

        $idata1 = [
            'name' => $data['name'],
            'code' => $data['code'],
            'updatetime' => time(),
            'tpl' => '',
            'logo' => '',
            'status' => (isset($data['status']) && $data['status']) ? 1 : 0,
            'website' => isset($data['website']) ? $data['website'] : '',
            'scanbg' => isset($data['scanbg']) ? intval($data['scanbg']) : 0,
            'displayorder' => 0,
        ];
        $this->db->table($this->tablename)->insert($idata1);
        $id = $this->db->insertID();
        if ($id) {
            //附件归属
            if (isset($data['thumb']) && $data['thumb']) {
                $related = $this->tablename . '-' . $id;
                $this->db->table('attachment')->where('id', intval($data['status']))->update(['related' => $related]);
            }
            return ams_rt(0, '操作成功', $id);
        }
        return ams_rt(1, '操作失败', $idata1);
    }

    public function edit($id, $data)
    {
        if (!$id) {
            return ams_rt(1, '参数错误');
        }
        $rt = $this->_validate($data, $id);
        if ($rt['code']) {
            return $rt;
        }

        $idata1 = [
            'name' => $data['name'],
            'code' => $data['code'],
            'updatetime' => time(),
            'status' => (isset($data['status']) && $data['status']) ? 1 : 0,
            'website' => isset($data['website']) ? $data['website'] : '',
            'scanbg' => isset($data['scanbg']) ? intval($data['scanbg']) : 0,
        ];

        $flag = $this->db->table($this->tablename)->where('id', $id)->update($idata1);

        if ($flag) {
            //附件归属
            if (isset($data['thumb']) && $data['thumb']) {
                $related = $this->tablename . '-' . $id;
                $this->db->table('attachment')->where('id', intval($data['status']))->update(['related' => $related]);
            }
            return ams_rt(0, '操作成功', $id);
        }
        return ams_rt(1, '操作失败', $idata1);
    }

    public function get($id)
    {
        if (!$id) {
            return ams_rt(1, '参数错误');
        }
        $row = $this->db->table($this->tablename)->where('id', $id)->get()->getRowArray();
        if (!$row) {
            return ams_rt(1, '数据不存在', []);
        }
        if (isset($row['thumb']) && $row['thumb']) {
            $row['thumb_info'] = $this->db->table('attachment')->select('filename,attachment')->where('id', intval($row['thumb']))->get()->getRowArray();
        }
        return ams_rt(0, 'ok', $row);
    }

    public function del($id)
    {
        if (!$id) {
            return ams_rt(1, '参数错误');
        }
        $row = $this->db->table($this->tablename)->select('id,title')->where('id', $id)->get()->getRowArray();
        if (!$row) {
            return ams_rt(1, '数据不存在', []);
        }
        $this->db->table($this->tablename)->where('id', $id)->delete();
        return ams_rt(0, 'ok');
    }

    public function switchDefault($id, $value)
    {
        if (!$id) {
            return ams_rt(1, '参数错误');
        }
        $row = $this->db->table($this->tablename)->select('id,name,default,status')->where('id', $id)->get()->getRowArray();
        if (!$row) {
            return ams_rt(1, '数据不存在');
        } elseif ($row['status'] == 0 && $value == 1) {
            return ams_rt(1, '设为默认,必须是启用状态');
        }
        if ($value == 1) {
            $this->db->table($this->tablename)->update(['default' => 0]);
            $this->db->table($this->tablename)->where('id', $id)->update(['default' => 1]);
        } else {
            $this->db->table($this->tablename)->where('id', $id)->update(['default' => 0]);
        }
        return ams_rt(0, '操作成功', $row);
    }

    // 分页
    public function limit_page($page = 0, $size = 10, $total = 0, $param = [])
    {

        $page = max(1, (int) $page);
        $total = (int) $total;

        unset($param['page']);
        if ($param) {
            $param = esc($param);
        }

        if ($size > 0 && !$total) {
            $select = $this->db->table($this->tablename)->select('count(*) as total');
            $param = $this->_limit_page_where($select, $param);
            $query = $select->get();
            if (!$query) {
                log_notice('error', '数据查询失败：' . $this->tablename);
                return [[], $total, $param];
            }
            $data = $query->getRowArray();
            $total = (int) $data['total'];
            $param['total'] = $total;
            unset($select);
            if (!$total) {
                return [[], $total, $param];
            }
        }

        $select = $this->db->table($this->tablename);
        $order = 'id desc';
        $param = $this->_limit_page_where($select, $param);
        $size > 0 && $select->limit($size, $size * ($page - 1));
        $query = $select->orderBy($order)->get();
        if (!$query) {
            log_notice('error', '数据查询失败：' . $this->tablename);
            return [[], $total, $param];
        }
        $data = $query->getResultArray();
        $param['order'] = $order;
        $param['total'] = $total;

        return [$data, $total, $param];

    }

    /**
     * 条件查询
     *
     * @param object $select 查询对象
     * @param intval $where 是否搜索
     * @return    intval
     */
    protected function _limit_page_where(&$select, $param)
    {

        // 条件搜索
        if ($param) {

            if (isset($param['name']) && $param['name']) {
                $select->where('name', $param['name']);
            }

            if (isset($param['phone']) && $param['phone']) {
                $select->where('phone', $param['phone']);
            }

            if (isset($param['status']) && ($param['status'] || $param['status'] == '0')) {
                $select->where('status', $param['status']);
            }
        }
        return $param;
    }

    /**
     * 条件查询
     */
    private function _where_tpl(&$select, $data)
    {
        if ($data) {

            if (isset($data['type']) && $data['type']) {
                $select->where('type', $data['type']);
            }

            if (isset($data['code']) && $data['code']) {
                $select->where('code', $data['code']);
            }

            if (isset($data['name']) && $data['name']) {
                $select->like('name', $data['name'], 'both');
            }

            if (isset($data['status']) && ($data['status'] == 1 || $data['status'] == '0')) {
                $select->where('status', $data['status']);
            }

            // 时间搜索
            if (isset($data['start']) && $data['start']) {
                $data['end'] = strtotime(date('Y-m-d 23:59:59', $data['end'] ? $data['end'] : time()));
                $data['start'] = strtotime(date('Y-m-d 00:00:00', $data['start']));
                $select->where('addtime BETWEEN ' . $data['start'] . ' AND ' . $data['end']);
            } elseif (isset($data['end']) && $data['end']) {
                $data['end'] = strtotime(date('Y-m-d 23:59:59', $data['end']));
                $data['start'] = 1;
                $select->where('addtime BETWEEN ' . $data['start'] . ' AND ' . $data['end']);
            }
        }
        return $data;
    }

    /**
     * 数据分页显示
     */
    public function limit_page_tpl($page = 0, $size = 10, $total = 0, $param = [])
    {
        ams_clean_xss($param);

        $page = max(1, (int) $page);
        $total = (int) $total;
        unset($param['page']);
        if ($size > 0 && !$total) {
            $select = $this->db->table($this->tablename . '_tpl')->select('count(*) as total');
            $param = $this->_where_tpl($select, $param);
            $query = $select->get();
            if (!$query) {
                log_message('error', '数据查询失败：' . $this->tablename . '_tpl');
                return [[], $total, $param];
            }
            $data = $query->getRowArray();
            $total = (int) $data['total'];
            $param['total'] = $total;
            unset($select);
            if (!$total) {
                return [[], $total, $param];
            }
        }

        $select = $this->db->table($this->tablename . '_tpl');
        $param = $this->_where_tpl($select, $param);
        $size > 0 && $select->limit($size, $size * ($page - 1));
        $query = $select->orderBy('id', 'asc')->get();
        if (!$query) {
            log_message('error', '数据查询失败：' . $this->tablename . '_tpl');
            return [[], $total, $param];
        }
        $data = $query->getResultArray();
        if ($data) {
            foreach ($data as $k => $v) {
                $data[$k]['addtime_date'] = date('Y-m-d H:i:s', $v['addtime']);
            }
        }
        $param['total'] = $total;
        return [$data, $total, $param];
    }

    //通知类型
    public function getAllType()
    {
        $data = [
            'notice' => '站内通知',
            'email' => '邮件通知',
            'sms' => '短信通知',
            'weixin' => '微信通知',
        ];
        return $data;
    }

    //新增和编辑全部数据时验证
    private function _validate_tpl($data, $id = 0)
    {
        if (!$data) {
            return ams_rt(1, '参数错误');
        }
        // ams_clean_xss($data);

        if (!$data['name']) {
            return ams_rt(1, '名称必须填写');
        }

        $flag = $this->field_exitsts($this->tablename . '_tpl', 'name', $data['name'], $id);
        if ($flag) {
            return ams_rt(1, '名称已存在');
        }

        if (isset($data['event']) && $data['event']) {
            $flag = $this->field_exitsts($this->tablename . '_tpl', 'event', $data['event'], $id);
            if ($flag) {
                return ams_rt(1, '事件已存在');
            }
        }

        $type = $this->getAllType();
        foreach ($type as $k => $v) {
            if (isset($data[$k])) {
                $data[$k] = json_encode($data[$k]);
            }
        }

        return ams_rt(0, 'ok', $data);
    }

    //
    public function getCats()
    {
        $rows = $this->db->table($this->tablename . '_tpl')->select('id,event,name')->get()->getResultArray();
        if (!$rows) {
            $rows = [];
        }
        return $rows;
    }

    //添加消息模板
    public function addTpl($data)
    {
        $odata = $data;
        $rt = $this->_validate_tpl($data);
        if ($rt['code']) {
            return $rt;
        }
        $data = $rt['data'];
        $data['addtime'] = time();
        $this->db->table($this->tablename . '_tpl')->insert($data);
        $id = $this->db->insertID();
        if ($id) {
            unset($odata);
            $data['id'] = $id;
            return ams_rt(0, '操作成功', $data);
        }
        return ams_rt(1, '操作失败', $odata);
    }

    //修改消息模板
    public function editTpl($id, $data)
    {
        $odata = $data;
        $id = intval($id);
        $row = $this->db->table($this->tablename . '_tpl')->where('id', $id)->get()->getRowArray();
        if (!$row) {
            return ams_rt(1, '数据不存在');
        }
        $rt = $this->_validate_tpl($data, $id);
        if ($rt['code']) {
            return $rt;
        }
        $data = $rt['data'];

        $data['addtime'] = time();
        $flag = $this->db->table($this->tablename . '_tpl')->where('id', $id)->update($data);

        return ams_rt(0, '操作成功', $odata);
    }

    //删除消息模板
    public function delTpl($id)
    {
        if (!$id) {
            return ams_rt(1, '参数错误');
        }
        $row = $this->db->table($this->tablename . '_tpl')->select('id,name,event')->where('id', $id)->get()->getRowArray();
        if (!$row) {
            return ams_rt(1, '数据不存在', []);
        }
        $this->db->table($this->tablename . '_tpl')->where('id', $id)->delete();
        return ams_rt(0, '操作成功', $row);
    }

    //消息总开关
    public function switchStatus($id)
    {
        $id = intval($id);
        if (!$id) {
            return ams_rt(1, '参数错误');
        }
        $row = $this->db->table($this->tablename . '_tpl')->select('id,name,event,status')->where('id', $id)->get()->getRowArray();
        if (!$row) {
            return ams_rt(1, '数据不存在');
        }
        if ($row['status'] == 1) {
            $status = 0;
        } else {
            $status = 1;
        }
        $this->db->table($this->tablename . '_tpl')->where('id', $id)->update(['status' => $status]);
        return ams_rt(0, '操作成功', $row);
    }

    //获取
    public function getInfoById($id)
    {
        $id = intval($id);
        $row = $this->db->table($this->tablename . '_tpl')->where('id', $id)->get()->getRowArray();
        if (!$row) {
            return [];
        }
        $type = $this->getAllType();
        foreach ($type as $k => $v) {
            if (isset($row[$k])) {
                $row[$k] = json_decode($row[$k], true);
            }
        }
        $row['addtime_date'] = date('Y-m-d H:i', $row['addtime']);
        return $row;
    }

    public function getInfoByEvent($event)
    {
        ams_clean_xss($event);
        $row = $this->db->table($this->tablename . '_tpl')->where('event', $event)->get()->getRowArray();
        if (!$row) {
            return [];
        }
        $type = $this->getAllType();
        foreach ($type as $k => $v) {
            if (isset($row[$k])) {
                $row[$k] = json_decode($row[$k], true);
            }
        }
        $row['addtime_date'] = date('Y-m-d H:i:s', $row['addtime']);
        return $row;
    }

    /**
     * 给用户发一条通知
     * @param   string  $uid
     * @param  类型：1系统 2用户 3内容 4应用 5交易 6订单
     * @param   string  $note
     * @return  null
     */
    public function sendNoticeToUser($userid, $type = 1, $note, $url = '')
    {
        if (!$userid || !$note) {
            return ams_rt(1, '参数错误');
        }
        $yes = $no = 0;
        $uids = is_array($userid) ? $userid : explode(',', $userid);
        foreach ($uids as $userid) {
            $this->db->table('user_notice')->insert([
                'userid' => $userid,
                'type' => max(1, (int) $type),
                'isnew' => 1,
                'content' => $note,
                'url' => (string) $url,
                'inputtime' => time(),
            ]);
            $id = $this->db->insertID();
            if ($id) {
                $yes++;
            } else {
                $no++;
            }
        }
        return ams_rt(0, '操作结果:成功' . $yes . ',失败' . $no);
    }

    /**
     * 给管理员发送系统提醒
     *
     * @param   type    system系统  content内容相关  member会员相关 app应用相关 pay 交易相关
     * @param   msg     提醒内容
     * @param   uri     后台对应的链接
     * @param   to      通知对象 留空表示全部对象
     * array(
     *      to_userid 指定人
     *      to_roleid 指定角色组
     * )
     */
    public function sendNoticeToAdmin($type, $user, $msg = '', $uri = '', $to = [])
    {
        ams_clean_xss($msg);
        if (!$type || !$user || !$msg) {
            return ams_rt(1, '参数错误');
        }
        $data = [
            'type' => $type,
            'msg' => $msg,
            'uri' => $uri,
            'to_roleid' => $to['to_roleid'] ?? 0,
            'to_userid' => $to['to_userid'] ?? 0,
            'status' => 0,
            'uid' => (int) $user['id'],
            'username' => $user['username'] ? $user['username'] : '',
            'op_userid' => 0,
            'op_username' => '',
            'updatetime' => 0,
            'inputtime' => time(),
        ];
        $this->db->table('admin_notice')->insert($data);
        $id = $this->db->insertID();
        if ($id) {
            return ams_rt(1, '操作成功');
        }
        return ams_rt(1, '操作失败');
    }

    /*************以下队列任务相关***************/

    /**
     * 发送通知动作（按用户设置)
     * $name    动作名称
     * $data    传入参数
     */
    public function send_notice($name, $data, $param = [])
    {
        $config = $this->getInfoByEvent($name);
        if (!$config) {
            return; // 没有配置通知
        }
        // 当前的信息变量
        $data['sys_app'] = MODDIR;
        $data['sys_uri'] = AMS_NOW_URL;
        $data['sys_time'] = time();
        $data['ip_address'] = $this->request->getIPAddress();
        // 自定义参数累加进去
        if ($param) {
            $data = array_merge($data, $param);
        }
        // 加入队列并执行
        $cronModel = new CronModel();
        $rt = $cronModel->add_cron('notice', [
            'name' => $name,
            'data' => $data,
            'config' => $config,
        ]);
        if (!$rt['code']) {
            log_message('error', '添加通知任务失败：' . $rt['msg']);
        }
        return;
    }

    /**
     * 发送通知动作（按自定义位置设置）
     * $name    动作名称
     * $data    传入参数
     */
    public function send_notice_user($name, $uid, $data, $config)
    {

        if (!$config) {
            return; // 没有配置通知
        }

        $data['userid'] = $uid;
        // 当前的信息变量
        $data['sys_time'] = time();
        $data['ip_address'] = $this->request->getIPAddress();
        // 加入队列并执行
        $cronModel = new CronModel();
        $rt = $cronModel->add_cron('notice', [
            'name' => $name,
            'data' => $data,
            'config' => $config,
        ]);
        if (!$rt['code']) {
            log_message('error', '添加通知任务失败：' . $rt['msg']);
        }

        return;
    }

    // 按类型执行最后的通知发送
    public function cron_notice($value)
    {
        $userModel = new UserModel();

        $error = [];
        if (!$value['data']['userid']) {
            log_message('error', '通知任务（' . $value['name'] . '）执行失败：用户uid参数为空，任务不能执行');
            return [['用户uid参数为空，任务不能执行'], $value];
        }

        $user = $userModel->getUser($value['data']['userid']);

        if (!$user) {
            log_message('error', '通知任务（' . $value['name'] . '）执行失败：用户uid(' . $value['data']['userid'] . ')账号不存在，任务不能执行');
            ams_write_log('cron_notice', '用户uid(' . $value['data']['userid'] . ')账号不存在，任务不能执行');
            return [['用户uid(' . $value['data']['userid'] . ')账号不存在，任务不能执行'], $value];
        }

        // 微信通知
        if (isset($value['config']['weixin']) && $value['config']['weixin']['open'] && $value['config']['weixin']['tpl']) {
            $tpl = $value['config']['weixin']['tpl'];
            $xml = $this->_xml_array($tpl);
            if (!$xml || !isset($xml['xml']) || !$xml['xml']) {
                $error[] = $debug = 'xml解析失败，检查文件格式是否正确：' . $value['name'] . '.html';
                log_message('error', '通知任务（' . $value['name'] . '）微信执行失败：' . $debug);
            } else {
                $content = $xml['xml'];
                $rt = $userModel->wexin_template($value['data']['userid'], $content['id'], $content['param'], $content['url']);
                if (!$rt['code']) {
                    $error[] = $debug = '微信消息执行错误：' . $rt['msg'];
                    log_message('error', '通知任务（' . $value['name'] . '）微信执行失败：' . $debug);
                } else {
                    // 成功
                    unset($value['config']['weixin']);
                }
            }
            if (isset($error) && $error) {
                ams_write_log('weixin', $error);
            }
        }

        // 短信通知
        if (isset($value['config']['sms']) && $value['config']['sms']['open'] && $value['config']['sms']['tpl']) {
            $phone = $user['phone'];
            if (!$phone) {
                $error[] = $debug = '用户【' . $user['username'] . '】phone参数为空，不能发送短信';
                log_message('error', '通知任务（' . $value['name'] . '）短信执行失败：' . $debug);
            } else {
                $content = $tpl = $value['config']['sms']['tpl'];
                $smsLib = new \App\Libraries\TencentSms();
                $code = mt_rand(100000, 999999) . '';
                $res = $smsLib->doaction('user_login', $phone, [$code]);
                if ($rt['code']) {
                    $error[] = $debug = '短信通知执行错误：' . $rt['msg'];
                    log_message('error', '通知任务（' . $value['name'] . '）短信执行失败：' . $debug);
                } else {
                    // 成功
                    unset($value['config']['mobile']);
                }
            }
            if (isset($error) && $error) {
                ams_write_log('sms', $error);
            }
        }

        // 站内消息通知
        if (isset($value['config']['notice']) && $value['config']['notice']['open'] && $value['config']['notice']['tpl']) {
            $content = $tpl = $value['config']['notice']['tpl'];
            $this->sendNoticeToUser($value['data']['userid'], max((int) $value['data']['type'], 1), $content, $value['data']['url']);
            // 成功
            unset($value['config']['notice']);
        }

        // 邮件通知
        if (isset($value['config']['email']) && $value['config']['email']['open'] && $value['config']['email']['tpl']) {
            $email = $user['email'];
            if (!$email) {
                $error[] = $debug = '用户【' . $user['username'] . '】的email参数为空，不能发送邮件';
                log_message('error', '通知任务（' . $value['name'] . '）邮件执行失败：' . $debug);
            } else {
                $content = $tpl = $value['config']['email']['tpl'];
                $title = '';
                if (preg_match('/<title>(.+)<\/title>/U', $content, $mt)) {
                    $title = $mt[1];
                    $content = str_replace($mt[0], '', $content);
                }
                $rt = $userModel->sendmail($email, $title ? $title : '通知', $content);
                if (!$rt['code']) {
                    $error[] = $debug = '邮件发送失败：' . $rt['msg'];
                    log_message('error', '通知任务（' . $value['name'] . '）邮件执行失败：' . $debug);
                } else {
                    // 成功
                    unset($value['config']['email']);
                }
            }
            if (isset($error) && $error) {
                ams_write_log('email', $error);
            }
        }

        return [$error, $error ? $value : []];
    }

    // xml转换数组
    private function _xml_array($xml)
    {
        $reg = "/<(\\w+)[^>]*?>(.*?)<\\/\\1>/Us";
        if (preg_match_all($reg, $xml, $matches)) {
            $count = count($matches[0]);
            $arr = array();
            for ($i = 0; $i < $count; $i++) {
                $key = $matches[1][$i];
                $val = $this->_xml_array($matches[2][$i]); // 递归
                if (array_key_exists($key, $arr)) {
                    if (is_array($arr[$key])) {
                        if (!array_key_exists(0, $arr[$key])) {
                            $arr[$key] = array($arr[$key]);
                        }
                    } else {
                        $arr[$key] = array($arr[$key]);
                    }
                    $arr[$key][] = $val;
                } else {
                    $arr[$key] = $val;
                }
            }
            return $arr;
        } else {
            return $xml;
        }
    }

    /**
     * 发送微信通知模板
     *
     * $uid 会员id
     * $id  微信模板id
     * $data    通知内容
     * $url 详细地址
     * $color   top颜色
     */
    public function weixin_template($uid, $id, $data, $url = '', $color = '')
    {

        $oauth = $this->db->table('user_oauth')->where('uid', $uid)->where('oauth', 'wechat')->get()->getRowArray();
        if (!$oauth) {
            return ams_rt(1, 'uid（' . $uid . '）未绑定账号');
        }

        $rt = $this->_weixin_template_access_token($id);
        if ($rt['code']) {
            return ams_rt(1, $rt['msg']);
        }
        $p = [
            'touser' => $oauth['oid'],
            'template_id' => $id,
            'url' => $url,
            'topcolor' => $color,
            'data' => $data,
        ];
        $rt = wx_post_https_json_data('https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' . $rt['data']['access_token'], $p);

        return $rt;
    }

    /**
     * 获取微信 access token
     */
    public function _weixin_template_access_token()
    {

        $cache_data = $this->cache->get('access_token');
        if (isset($cache_data['time']) && $cache_data['access_token']) {
            $access_token = $cache_data['access_token'];
        } else {
            $weixin = \Phpcmf\Service::C()->get_cache('weixin'); //配置文件
            $result = wx_get_https_json_data('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $weixin['account']['appid'] . '&secret=' . $weixin['account']['appsecret']);
            if (!$result['code']) {
                return ams_rt(1, 'AccessToken：' . $result['msg']);
            }
            $rt = $this->cache->save('access_token', $result['data'], 3600);
            if (!$rt) {
                return ams_rt(1, 'AccessToken：/cache/temp/目录不可写，文件写入失败');
            }
            $access_token = $result['data']['access_token'];
        }

        return ams_rt(0, 'ok', ['access_token' => $access_token]);
    }

}
